package room

import (
	"errors"
	"server/common"
	"sync"
	"time"
)

type RoomManager struct {
	rooms         map[string]*Room // key: room_id
	mtx           sync.RWMutex     // Mutex to protect access to the rooms map
	dispatcher    RoomDispatcher   // Message dispatcher for handling room-related messages
	serviceInfo   common.ServiceInfo
	mediaMgr      *MediaManager          // Media manager for handling media-related operations
	destroyTimers map[string]*time.Timer // Map to hold destroy timers for rooms
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func NewRoomManager(d common.IMsgDispatcher, s common.ServiceInfo, m *MediaManager) *RoomManager {
	return &RoomManager{
		rooms:       make(map[string]*Room),
		dispatcher:  RoomDispatcher{FromService: s, ToService: common.AccessServiceInfo, Dispatcher: d},
		serviceInfo: s,
		mediaMgr:    m,
	}
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) GetRoom(roomId string) *Room {
	rm.mtx.RLock()
	defer rm.mtx.RUnlock()

	if room, exists := rm.rooms[roomId]; exists {
		return room
	}
	return nil
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) startDestroyTimer(roomId string, delay time.Duration) {
	if rm.destroyTimers == nil {
		rm.destroyTimers = make(map[string]*time.Timer)
	}
	// 如果已有定时器，先取消
	rm.cancelDestroyTimer(roomId)
	timer := time.AfterFunc(delay, func() {
		LOG().Infof("Room %s is empty, starting destroy timer", roomId)
		rm.mtx.Lock()
		defer rm.mtx.Unlock()
		// 再次确认房间是否还需要销毁
		room, exists := rm.rooms[roomId]
		if !exists {
			LOG().Infof("Room %s does not exist, stopping destroy timer", roomId)
			return
		}
		if len(room.Users) == 0 {
			if err := room.Destroy(); err != nil {
				LOG().WithError(err).Errorf("Failed to destroy room %s", roomId)
			}
			delete(rm.rooms, roomId)
			delete(rm.destroyTimers, roomId)
			LOG().Infof("Room %s destroyed due to inactivity", roomId)
		}
	})
	rm.destroyTimers[roomId] = timer
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) cancelDestroyTimer(roomId string) {
	if rm.destroyTimers == nil {
		return
	}
	if timer, exists := rm.destroyTimers[roomId]; exists {
		timer.Stop()
		delete(rm.destroyTimers, roomId)
		LOG().Infof("Stopped destroy timer for room %s", roomId)
	}
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) CreateRoom(rid string, roomId string, userId string) (*Room, error) {
	rm.mtx.Lock()
	defer rm.mtx.Unlock()

	if _, exists := rm.rooms[roomId]; exists {
		return nil, errors.New("room already exists")
	}

	room := NewRoom(rid, roomId, userId, userId, rm.mediaMgr, rm.dispatcher)
	if room == nil {
		return nil, errors.New("failed to create room")
	}

	rm.rooms[roomId] = room

	// Start the destroy timer for the room
	rm.startDestroyTimer(roomId, 60*time.Second)

	return room, nil
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) GetUserList(roomId string) []RoomUser {
	rm.mtx.RLock()
	defer rm.mtx.RUnlock()

	room, exists := rm.rooms[roomId]
	if !exists {
		return []RoomUser{}
	}

	return room.GetUserList()
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) GetUser(roomId string, userId string) (*RoomUser, error) {
	rm.mtx.RLock()
	defer rm.mtx.RUnlock()

	room, exists := rm.rooms[roomId]
	if !exists {
		return nil, errors.New("room does not exist")
	}

	return room.GetUser(userId)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) AddUserToRoom(rid string, roomId string, user RoomUser) error {
	rm.mtx.Lock()
	defer rm.mtx.Unlock()

	room, exists := rm.rooms[roomId]
	if !exists {
		return errors.New("room does not exist")
	}

	if err := room.AddUser(user); err != nil {
		LOG(rid).WithError(err).Error("failed to add user to room")
		return err
	}

	rm.cancelDestroyTimer(roomId) // Cancel destroy timer when a user joins

	return nil
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) RemoveUserFromRoom(rid string, roomId string, userId string) error {
	rm.mtx.Lock()
	defer rm.mtx.Unlock()

	room, exists := rm.rooms[roomId]
	if !exists {
		return errors.New("room does not exist")
	}

	room.RemoveUser(userId)

	// If the room is empty, start the destroy timer
	if room.IsEmpty() {
		rm.startDestroyTimer(roomId, 60*time.Second)
		LOG(rid).Infof("Room %s is empty, starting destroy timer", roomId)
	}

	return nil
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) OnUserOffline(userId string) {
	rm.mtx.Lock()
	defer rm.mtx.Unlock()

	for roomId, room := range rm.rooms {
		if room.HasUser(userId) {
			room.RemoveUser(userId)
			LOG().Infof("User %s removed from room %s due to offline status", userId, roomId)

			// If the room is empty, start the destroy timer
			if room.IsEmpty() {
				rm.startDestroyTimer(roomId, 60*time.Second)
				LOG().Infof("Room %s is empty, starting destroy timer", roomId)
			}
		}
	}
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) RoomExists(roomId string) bool {
	rm.mtx.RLock()
	defer rm.mtx.RUnlock()

	_, exists := rm.rooms[roomId]
	return exists
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) UserExists(roomId string, userId string) bool {
	rm.mtx.RLock()
	defer rm.mtx.RUnlock()

	room, exists := rm.rooms[roomId]
	if !exists {
		return false
	}

	return room.HasUser(userId)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) ProducerExists(roomId string, mediaId string) bool {
	rm.mtx.RLock()
	defer rm.mtx.RUnlock()

	room, exists := rm.rooms[roomId]
	if !exists {
		return false
	}

	return room.ProducerExists(mediaId)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) ConsumerExists(roomId string, userId string, mediaId string) bool {
	rm.mtx.RLock()
	defer rm.mtx.RUnlock()

	room, exists := rm.rooms[roomId]
	if !exists {
		return false
	}

	return room.ConsumerExists(userId, mediaId)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) CreateProducer(rid string, data common.SFUStartProduceReqData) (*Producer, error) {
	rm.mtx.Lock()
	defer rm.mtx.Unlock()

	room, exists := rm.rooms[data.RoomId]
	if !exists {
		return nil, errors.New("room does not exist")
	}

	return room.CreateProducer(rid, data)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) CloseProducer(rid string, data common.SFUStopProduceReqData) error {
	rm.mtx.Lock()
	defer rm.mtx.Unlock()

	room, exists := rm.rooms[data.RoomId]
	if !exists {
		return errors.New("room does not exist")
	}

	return room.CloseProducer(rid, data)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) CreateConsumer(rid string, data common.SFUStartSubscribeReqData) (*Consumer, error) {
	rm.mtx.Lock()
	defer rm.mtx.Unlock()

	room, exists := rm.rooms[data.RoomId]
	if !exists {
		return nil, errors.New("room does not exist")
	}

	return room.CreateConsumer(rid, data)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) CloseConsumer(rid string, data common.SFUStopSubscribeReqData) error {
	rm.mtx.Lock()
	defer rm.mtx.Unlock()

	room, exists := rm.rooms[data.RoomId]
	if !exists {
		return errors.New("room does not exist")
	}

	return room.CloseConsumer(rid, data)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) GetProducer(roomId string, userId string, mediaId string) (*Producer, error) {
	rm.mtx.RLock()
	defer rm.mtx.RUnlock()

	room, exists := rm.rooms[roomId]
	if !exists {
		return nil, errors.New("room does not exist")
	}

	return room.GetProducer(userId, mediaId)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) GetConsumer(roomId string, userId string, mediaId string) (*Consumer, error) {
	rm.mtx.RLock()
	defer rm.mtx.RUnlock()

	room, exists := rm.rooms[roomId]
	if !exists {
		return nil, errors.New("room does not exist")
	}

	return room.GetConsumer(userId, mediaId)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) SendTransportExists(roomId string, userId string) bool {
	rm.mtx.RLock()
	defer rm.mtx.RUnlock()

	room, exists := rm.rooms[roomId]
	if !exists {
		return false
	}

	return room.SendTransportExists(userId)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) RecvTransportExists(roomId string, userId string) bool {
	rm.mtx.RLock()
	defer rm.mtx.RUnlock()

	room, exists := rm.rooms[roomId]
	if !exists {
		return false
	}

	return room.RecvTransportExists(userId)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) MatchRecvTransport(roomId string, userId string, transportId string) bool {
	rm.mtx.RLock()
	defer rm.mtx.RUnlock()

	room, exists := rm.rooms[roomId]
	if !exists {
		return false
	}

	return room.MatchRecvTransport(userId, transportId)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) MatchSendTransport(roomId string, userId string, transportId string) bool {
	rm.mtx.RLock()
	defer rm.mtx.RUnlock()

	room, exists := rm.rooms[roomId]
	if !exists {
		return false
	}

	return room.MatchSendTransport(userId, transportId)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) CreateTransport(rid string, data common.SFUCreateTransportReqData) (
	*Transport, error) {
	rm.mtx.Lock()
	defer rm.mtx.Unlock()

	room := rm.rooms[data.RoomId]
	if room == nil {
		return nil, errors.New("room not found")
	}

	return room.CreateTransport(rid, data)
}

// -------------------------------------------------------------------------------------------------
//
// -------------------------------------------------------------------------------------------------
func (rm *RoomManager) ConnectTransport(rid string, data common.SFUConnectTransportReqData) error {
	rm.mtx.Lock()
	defer rm.mtx.Unlock()

	room := rm.rooms[data.RoomId]
	if room == nil {
		return errors.New("room not found")
	}

	return room.ConnectTransport(rid, data)
}
